package com.own.component.store.local.method.state;

import lombok.extern.slf4j.Slf4j;

import java.time.Duration;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * StoreState
 *
 * @author chenxueli
 * @date 2023-07-10 14:11:25
 */
@Slf4j
public class StoreState<T> {

    /**
     * 状态存储（无超时数据）
     */
    private final Map<String, T> STATE_STORE = new ConcurrentHashMap<>(256);
    /**
     * 状态存储（有超时数据）
     */
    private final Map<String, StateExpire<T>> STATE_EXPIRE_STORE = new ConcurrentHashMap<>(256);

    /**
     * 获取数据
     *
     * @param key 键
     * @return 值
     */
    public T get(String key) {
        if (STATE_STORE.containsKey(key)) {
            return STATE_STORE.get(key);
        }
        if (STATE_EXPIRE_STORE.containsKey(key)) {
            var state = STATE_EXPIRE_STORE.get(key);
            if (!state.isExpired()) {
                return STATE_EXPIRE_STORE.get(key).getState();
            }
            STATE_EXPIRE_STORE.remove(key);
        }
        return null;
    }

    /**
     * 设置数据
     *
     * @param key   键
     * @param value 值
     */
    public void set(String key, T value) {
        STATE_EXPIRE_STORE.remove(key);
        STATE_STORE.put(key, value);
    }

    /**
     * 设置数据值
     *
     * @param key      键
     * @param value    值
     * @param duration 超时时间
     */
    public void set(String key, T value, Duration duration) {
        if (duration == null || duration.toNanos() <= 0L) {
            set(key, value);
            return;
        }
        STATE_STORE.remove(key);
        STATE_EXPIRE_STORE.put(key, new StateExpire<>(value, duration));
    }

    /**
     * 设置数据超时
     *
     * @param key      键
     * @param duration 超时时间
     */
    public void expire(String key, Duration duration) {
        // 判断设置的超时时间信息
        if (duration == null || duration.toNanos() <= 0L) {
            // 设置成无期限
            if (STATE_STORE.containsKey(key)) {
                return;
            }
            // 移除有期限的数据，添加到无期限的位置
            if (STATE_EXPIRE_STORE.containsKey(key)) {
                STATE_STORE.put(key, STATE_EXPIRE_STORE.get(key).getState());
                STATE_EXPIRE_STORE.remove(key);
            }
        } else {
            // 移除无期限的数据，添加到有期限的位置
            if (STATE_STORE.containsKey(key)) {
                STATE_EXPIRE_STORE.put(key, new StateExpire<>(STATE_STORE.get(key), duration));
                STATE_STORE.remove(key);
                return;
            }
            // 有期限数据续期
            if (STATE_EXPIRE_STORE.containsKey(key)) {
                STATE_EXPIRE_STORE.get(key).setExpire(duration);
            }
        }
    }

    /**
     * 移除数据
     *
     * @param key 键
     */
    public void remove(String key) {
        STATE_EXPIRE_STORE.remove(key);
        STATE_STORE.remove(key);
    }

    /**
     * 是否包含数据
     *
     * @param key 键
     * @return 是否包含
     */
    public boolean contains(String key) {
        if (STATE_STORE.containsKey(key)) {
            return true;
        }
        if (STATE_EXPIRE_STORE.containsKey(key)) {
            return !STATE_EXPIRE_STORE.get(key).isExpired();
        }
        return false;
    }

    /**
     * 获取所有键
     *
     * @return 键集合
     */
    public Set<String> keySet() {
        var set = new HashSet<String>();
        set.addAll(STATE_STORE.keySet());
        set.addAll(STATE_EXPIRE_STORE.keySet());
        return set;
    }

    /**
     * 清除过期缓存
     */
    public void pruneCache() {
        var originCount = STATE_EXPIRE_STORE.size();
        var values = STATE_EXPIRE_STORE.values().iterator();
        var count = 0;
        StateExpire<T> cacheState;
        while (values.hasNext()) {
            cacheState = values.next();
            if (cacheState.isExpired()) {
                values.remove();
                count++;
            }
        }
        log.debug("原有缓存：{}个，清除过期缓存:{}个", originCount, count);
    }

}
